/// @file parser.cc
/// @brief Parse input tree files loading DAG structure

// *************************************************************************

// Copyright© 2010 Texas A&M University, 
//                 College Station, Texas
//                 Contact: R Crosby <rwc@cs.tamu.edu>
//
// Copyright© 2003, 2004 by BiRC -- Bioinformatics Research Center
//                 University of Aarhus, Denmark
//                 Contact: Thomas Mailund <mailund@birc.dk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// *************************************************************************

#include <cstdio>
#include <fstream>

using namespace std;

#include "dag.hh"
#include "options.hh"
#include "parser.hh"
#include "rootedtree.hh"
#include "S.hh"
#include "unrootedtree-builder.hh"

int                     yylex();
extern unsigned int     current_line;
extern const char       *yytext;

const char              *leaf_name;

// *********************************************************************

static void
AddTree ( const char *             fname, 
          DAG &                    dag,    
          UnrootedTreeBuilder *&   builder,
          unsigned &               stLine,
	  SMap &                   smap )
{

    UnrootedTree * ut = builder->build_tree();
    RootedTree     rt ( ut, smap.RootLabel() );
    dag.merge         ( rt, fname, stLine );
    delete ut;

    delete builder;
    builder = new UnrootedTreeBuilder(smap);

    if (options::verbose) cout << ".";

    stLine    = 0;

}

// *********************************************************************

static void
parse_branch_length(const char *fname)
{
    switch (yylex())
    {
    case 'f':
	// ignore the actual length
	break;

    default:
	throw Parser::ParseError(fname, current_line);
    }
}

// *********************************************************************

void
Parser::parse_tree(const char * fname, 
                   DAG &        dag,
                   SMap &       smap )
{

    FILE *f = freopen(fname, "r", stdin);
    if (!f) throw FileOpenError(fname);

    current_line = 1;

    UnrootedTreeBuilder* builder = new UnrootedTreeBuilder(smap);
    unsigned int         stLine  = 0;

    enum States {
	EXPECTING_IDENTIFIER,
	AFTER_IDENTIFIER,
    };

    int state = EXPECTING_IDENTIFIER;
    int token;

    while ( ( token = yylex() ) && 
	    ( !options::tLimit || dag.Roots().size() < options::tLimit) ) {

        if (!stLine) stLine = current_line;

        switch (token) {

	case '(':
	    builder->push_internal();
	    state = EXPECTING_IDENTIFIER;
	    break;

	case ')':
	    builder->pop_internal();
	    state = EXPECTING_IDENTIFIER;
	    break;

	case ',':
	    state = EXPECTING_IDENTIFIER;
	    break;
		    
	case 'i':
	    builder->add_leaf(leaf_name);
	    state = AFTER_IDENTIFIER;
	    break;

	case 'f':
	    if (state == EXPECTING_IDENTIFIER) {
                builder->add_leaf(leaf_name);
		state = AFTER_IDENTIFIER;
            }
	    else {
                std::cerr << "Warning, ignoring float at line "
                          << current_line << std::endl;
            }
	    break;

	case ':':
	    parse_branch_length(fname);
	    break;

        case ';':
            AddTree ( fname, dag, builder, stLine, smap );
            break;

        default:
	    throw ParseError ( fname, current_line );
	}

    }

    fclose(f);

    if (stLine) AddTree ( fname, dag, builder, stLine, smap );

    delete builder;

}

